﻿using log4net;
using NHibernate.Audit.Cfg;
using NHibernate.Persister.Entity;
using System;
using System.Collections.Generic;

namespace NHibernate.Audit.Listener
{
    public class AuditEntityBuilder 
    {

        private ILog log = log4net.LogManager.GetLogger(typeof(AuditEntityBuilder));

        public AuditEntityBuilder(IEntityPersister realEntityPersister, 
            IEntityPersister auditEntityPersister, 
            AuditConfiguration auditConfiguration)
        {
            if (null == realEntityPersister)
                throw new ArgumentNullException("realEntityPersister");
            if (null == auditEntityPersister)
                throw new ArgumentNullException("auditEntityPersister");
            if (null == auditConfiguration)
                throw new ArgumentNullException("auditConfiguration");
            this.realEntityPersister = realEntityPersister;
            this.auditEntityPersister = auditEntityPersister;
            this.auditConfiguration = auditConfiguration;
        }

        private IEntityPersister realEntityPersister;
        private IEntityPersister auditEntityPersister;
        private AuditConfiguration auditConfiguration;

        public virtual IDictionary<string, object> BuildAuditEntityMap(object realEntity, EntityMode realEntityMode, object revisionId)
        {
            IDictionary<string, object> map = new Dictionary<string, object>();
            AddPropertiesToMap(map, realEntity, realEntityMode);
            map = FilterProperties(map);
            AddIdentifierToMap(map, realEntity, realEntityMode, revisionId);

            if (log.IsDebugEnabled)
                foreach (var item in map)
                    log.DebugFormat("{0}={1}", item.Key, item.Value);

            return map;
        }

        protected virtual void AddPropertiesToMap(IDictionary<string, object> map, 
            object realEntity, EntityMode realEntityMode)
        {
            var values = realEntityPersister.GetPropertyValues(realEntity, realEntityMode);
            realEntityPersister.SetPropertyValues(map, values, EntityMode.Map);
        }

        protected virtual void AddIdentifierToMap(IDictionary<string, object> map,
            object realEntity, EntityMode realEntityMode, object revisionId)
        {
            var revisionIdPropName = auditConfiguration.RevisionConfiguration.GetRevisionPropertyName(realEntityPersister.EntityName);
            var identifier = realEntityPersister.GetIdentifier(realEntity, realEntityMode);
            realEntityPersister.SetIdentifier(map, identifier, EntityMode.Map);
            map.Add(revisionIdPropName, revisionId);
        }

        protected virtual IDictionary<string, object> FilterProperties(IDictionary<string, object> realMap)
        {
            var classConv = auditConfiguration.ClassConvention;
            var auditMap = new Dictionary<string, object>();
            foreach (var key in realMap.Keys)
                if (classConv.AuditProperty(key))
                    auditMap.Add(key, realMap[key]);
            return auditMap;
        }


    }
}
